* ESTFE - Allow easy FE rows with estout
* See example at the end

capture program drop estfe
program define estfe
	syntax [anything(id="stored estimates" name=est_list)], [restore labels(string asis)]
	if ("`restore'"!="") Restore `est_list'
	else Add `est_list', labels(`labels')
end

capture program drop Add
program define Add, rclass
	syntax [anything(id="stored estimates" name=est_list)], [labels(string asis)]
	local dot .
	local hasdot : list dot in est_list
	local est_list : list est_list - dot

	if ("`est_list'"!="") {
		qui estimates dir `est_list'
		local models "`r(names)'"
	}

	if (`hasdot') {
		tempname hold
		estimates store `hold', nocopy
		local models `hold' `models'
	}

	foreach model of local models {
		AddOne `model' // injected `absvars'
		local fe_list `fe_list' `absvars'
	}
	local fe_list : list uniq fe_list

	if (`hasdot') {
		qui estimates restore `hold'
		estimates drop `hold'
	}

	local indicate_fe // This will contain our answer
	while (`"`labels'"'!="") {
		gettoken lhs labels : labels
		gettoken rhs labels : labels
		if "`rhs'"=="" {
			di as error "error: odd number of labels"
			error 123
		}

		foreach fe of local fe_list {
			local fixed_fe : subinstr local fe "0." "", all
			if ("`fixed_fe'"=="`lhs'") {
				local indicate_fe `"`indicate_fe' "`rhs'=`fe'" "'
				local fe_list : list fe_list - fe
				continue, break
			}
		}
	}

	* Parse remaining (w/out label)
	foreach fe of local fe_list {
		local fixed_fe : subinstr local fe "0." "", all
		local indicate_fe `"`indicate_fe' "`fixed_fe'=`fe'""'
	}

	return local indicate_fe `"`indicate_fe'"'
end

capture program drop AddOne
program define AddOne, eclass
	* From Ben Jann
	* See https://github.com/benjann/estout/issues/6
	* Requires erepost from SSC
	args model

	qui estimates restore `model'
	
	* Backup e(b) e(V)
	tempname b V new
	matrix `b' = e(b)
	matrix `V' = e(V)
	ereturn matrix b_backup = `b', copy
	ereturn matrix V_backup = `V', copy

	* Augment, reghdfe convention
	local K = colsof(`b')
	local G = e(N_hdfe_extended)
	local absvars "`e(extended_absvars)'"
	
	* Also allow areg
	if (`G'==.) local G = 1
	if ("`absvars'"=="") local absvars "`e(absvar)'"
	FixAbsvars `absvars'

	* Allow xtreg_fe, xtivreg_fe, etc
	if ("`absvars'"=="" & "`e(model)'"=="fe") local absvars "`e(ivar)'"

	matrix `new' = J(1, `G', 0)
	matrix colnames `new' = `absvars'
	matrix `b' = `b', `new'
	matrix `V' = (`V' , J(`K', `G', 0)) \ (J(`G', `K', 0), J(`G', `G', 0))

	erepost b=`b' V=`V', rename // Minor problem: removes "hidden" attribute
	estimates store `model', nocopy
	c_local absvars "`absvars'"
end

capture program drop FixAbsvars
program define FixAbsvars
	while ("`0'"!="") {
		gettoken absvar 0 : 0
		local newabsvar
		while ("`absvar'"!="") {
			gettoken part absvar : absvar, parse("# ")
			if (strpos("`part'", "#")==0 & strpos("`part'", "c.")==0) local part 0.`part'
			local newabsvar `newabsvar'`part'
		}
		local newabsvars `newabsvars' `newabsvar'
	}
	c_local absvars `newabsvars'
end

capture program drop Restore
program define Restore, eclass
	syntax [anything(id="stored estimates" name=est_list)]
	local dot .
	local hasdot : list dot in est_list
	local est_list : list est_list - dot

	if ("`est_list'"!="") {
		qui estimates dir `est_list'
		local models "`r(names)'"
	}

	if (`hasdot') {
		tempname hold
		estimates store `hold', nocopy
		local models `hold' `models'
	}

	foreach model of local models {
		qui estimates restore `model'
		tempname b V
		matrix `b' = e(b_backup)
		matrix `V' = e(V_backup)
		ereturn local b_backup
		ereturn local V_backup
		erepost b=`b' V=`V', rename
		estimates store `model', nocopy
	}

	if (`hasdot') {
		qui estimates restore `hold'
		estimates drop `hold'
	}
end

/*
* Setup
	pr drop _all
	set trace off
	clear all
	set more off
	sysuse auto

	bys turn: gen t = _n
	xtset turn t
	
* Run and store regressions
	reghdfe price weight, a(turn foreign#trunk##c.gear) keepsing
	estimates store model1, nocopy

	reghdfe price weight length, a(foreign turn) keepsing
	estimates store model2, nocopy
	
	areg price length, a(turn)
	estimates store model3, nocopy

	regress price weight
	estimates store model4, nocopy

	xtreg price gear, fe
	estimates store model5, nocopy

	xtreg price gear length, re
	*estimates store model6, nocopy

* Prepare estimates for -estout-
	estfe . model*, labels(turn "Turn FE" foreign#trunk "Foreign-Trunk FE" foreign#trunk#c.gear_ratio "Foreign-Trunk Gear Slope")
	return list
	
* Run estout/esttab
	esttab . model* , indicate("Length Controls=length" `r(indicate_fe)') varwidth(30)
		
* Return stored estimates to their previous state
	estfe . model*, restore

* Verify
	areg
	estimates dir _all
	estimates restore model2
	reghdfe
	di e(b_backup) // gives error if not restored
*/

